2024 最新 Flutter 教學 - Flutter 終極指南: 連結
從零開始學 Dart 程式設計: 連結
Flutter 程式設計入門實戰 30 天: 連結
哈囉~大家好,我是 KT ,今天【iT邦幫忙鐵人賽】挑戰第二十九天,KT 將為大家來介紹,Provider 狀態管理。
APP 應用程式常需要在不同的頁面中,共享應用程式的狀態資料,例如下方展示簡單的電商購物畫面,當使用者登入後,可以在商品介紹頁,挑選喜愛的商品,加入購物車,切換到購物車時可以看到剛剛加入的清單與項目,可以想像一下,當最後要結帳購買時,若使用者已登入,使用者不需要在最後結帳頁再次輸入帳號相關資料,會從一開始登入畫面時,就已經將相關狀態資料帶到結帳頁。簡單說就是在特定頁面新增或修改相關資料狀態,想要在其他頁面共享資料狀態,就可以透過今天狀態管理的功能來實作出此項功能。
若想學習此購物車範例應用,可以參考 Flutter 官方實作範例 github 原始碼:provider_shopper
狀態管理常應用在以下這些功能:
狀態管理我們通常會放在 widget 元件樹中的最上層,好方便可以輕鬆的傳遞到其他底層頁面,以上方購物車應用範例中,我們可以看到 cart 購物車狀態放在 widget 元件樹中的最上層。當我們在購物項目中變化時,前往購物車頁則可以獲取新的變化資料。
在 2019 Google I/O 官方大力推薦,狀態管理建議採用 Provider 套件來取代之前的 Provide 。提高程式碼維護性,使結構更清晰,更重要的是提供單向數據流,實現更小單元的刷新,大幅提升整體運作性能。
provider 有四個重要的概念:
主要用在向已註冊的監聽者發送通知,ChangeNotifier 會呼叫 notifyListeners() 來通知狀態已改變,換句話說你可以註冊你想監聽變化狀態,當狀態改變,則可以透過此收到更新通知。
主要用在向其他子節點,底層的頁面, 提供 ChangeNotifier 的實例 (instance),所以 ChangeNotifierProvider 需放在要訪問狀態的元件之上。而如果想提供多個狀態可以使用 MultiProvider。
當我們註冊的 ChangeNotifier 與 ChangeNotifierProvider關聯起來後,當發生狀態變化時,我們可以將要變化的資料透過 Consumer 來接收更改對應資料。而最好將 Consumer 放在 widget 樹較低的位置上。才不會 UI 上任何一點小變化,就需要全盤重新構建整個 widget。
因為透過 Consumer 更新資料會讓整體框架整個重構,有時我們只是需要訪問特定資料或調用特定方法,此時可以使用 Provider.of 並且將listen 設定為 false,則可以訪問特定數據,又不會讓整體 UI 框架重構。
在 pubspec.yaml 中加入 Provider 套件,
provider: ^3.1.0
PS. ^表示與當前大版號一致的版本,〜表示和當前小版號一致的版本。
我們將建立兩個頁面:HomePage 和 BPage。HomePage 會顯示目前計數值,另外有一個按鈕,點擊可以前往 BPage。BPage 點擊右下角按鈕,計數值將會累加一,返回 HomePage 則可以同步獲取剛剛的計數變化值。
新增一個檔名 MyCountChangeNotifier 的 Dart 檔案。
import 'package:flutter/foundation.dart';
class MyCountChangeNotifier with ChangeNotifier {
// 設定一個整數私有變數 _count的欄位,初值為零
int _count = 0;
//可以透過 Consumer 來獲得當下 count 值
int get count => _count;
//當點擊右下角+ 浮動按鈕,會呼叫此方法
//此方法會將 _count 累加 1,並叫 notifyListeners
increment() {
_count++;
notifyListeners();
}
}
//透過 Provider.of 來獲取資料
final counter = Provider.of<MyCountChangeNotifier>(context);
Consumer 接收一個參數、若有兩個參數使用 Consumer2,三個參數使用 Consumer3 以此類推,目前最多支援到 Consumer6,接收六個參數。
child: Consumer<MyCountChangeNotifier>(builder: (context, counter, _) {
...
...
...
}
import 'package:flutter/material.dart';
import 'MyCountChangeNotifier.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//在 widget 元件樹中的最上層,使用 provider ,方便傳遞到其他底層頁面
//KT 建議採用 MultiProvider,因為一個 APP 很少一個 provider 就夠用,所以直接上 MultiProvider 。
return MultiProvider(
providers: [ChangeNotifierProvider.value(value: MyCountChangeNotifier())],
child: MaterialApp(
home: HomePage(),
),
);
}
}
//
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
//透過 Provider.of 來獲取資料
final counter = Provider.of<MyCountChangeNotifier>(context);
return Scaffold(
appBar: AppBar(
title: Text('HKT 線上教室'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'目前計數值: ${counter.count}',
),
RaisedButton(
//點擊按鈕後,導轉跳到B頁
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => BPage()),
),
child: Text('跳到B頁'),
),
],
),
),
);
}
}
class BPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('B頁'),
),
body: Center(
// 透過 Consumer 來接收更改對應資料
child: Consumer<MyCountChangeNotifier>(builder: (context, counter, _) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'目前計數值:',
),
Text(
'${counter.count}',
),
],
);
}),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 使用 Provider.of,並且將 listen 設定為 false(若沒設定,預設為 true),
// 則不會再次調用 Widget 重新構建( build )畫面 ,更省效能。
Provider.of<MyCountChangeNotifier>(context, listen: false)
.increment();
},
child: const Icon(Icons.add),
),
);
}
}
import 'package:flutter/foundation.dart';
class MyCountChangeNotifier with ChangeNotifier {
// 設定一個整數私有變數 _count的欄位,初值為零
int _count = 0;
//可以透過 Consumer 來獲得當下 count 值
int get count => _count;
//當點擊右下角+ 浮動按鈕,會呼叫此方法
//此方法會將 _count 累加 1,並叫 notifyListeners
increment() {
_count++;
notifyListeners();
}
}
那今天【iT邦幫忙鐵人賽】就介紹到這邊囉~
順帶一提,KT 線上教室,臉書粉絲團,會不定期發佈相關資訊,不想錯過最新資訊,不要忘記來按讚,加追蹤喔!也歡迎大家將這篇文章分享給更多人喔。
我們明天見囉!!!掰掰~
HKT 線上教室
http://tw-hkt.blogspot.com/
Background vector created by freepik
https://www.freepik.com
Data & backend
https://flutter.dev/docs/development/data-and-backend
provider
https://github.com/rrousselGit/provider
简单的应用状态管理
https://flutter.cn/docs/development/data-and-backend/state-mgmt/simple
Flutter开始干系列-状态管理Provider3
https://juejin.im/post/5d82d3456fb9a06b1829f260